package com.lagou.filter;

import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.rpc.*;
import org.apache.dubbo.rpc.support.RpcUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.*;

//@Activate(group = {CommonConstants.CONSUMER})
public class TPMonitorFilter implements Filter {

  private static final Logger log = LoggerFactory.getLogger(TPMonitorFilter.class);

  private static Map<String, ArrayList<Monitor>> timeMap = new HashMap<>();


  /** 每5秒钟，打印最近一分钟T90请求耗时 */
  public static void printTP90Monitor() {
    while (true) {
      System.out.println(">>>>>>>>>>>>>>> 一轮T90打印开始 >>>>>>>>>>>>>>>");
      for (Map.Entry<String, ArrayList<Monitor>> entry : timeMap.entrySet()) {
        String key = entry.getKey();
        ArrayList<Monitor> list = entry.getValue();
        Object[] objects = list.toArray();
        Arrays.sort(objects, Collections.reverseOrder());
        int per = (int) (Math.ceil(objects.length * 0.9));
        System.out.println("方法：" + key + " , TP90最近一分钟被调用情况，耗时如下 >>>>>>> ：");
        printTime(objects, per);
      }
      System.out.println(">>>>>>>>>>>>>>> 一轮T90打印结束 >>>>>>>>>>>>>>>" + "\n");
      try {
        Thread.sleep(4000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }

      System.err.println(">>>>>>>>>>>>>>> 一轮T99打印开始 >>>>>>>>>>>>>>>");
      printTP99Monitor();
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
      System.err.println(">>>>>>>>>>>>>>> 一轮T99打印结束 >>>>>>>>>>>>>>>" + "\n");
    }
  }

  private static void printTime(Object[] objects, int per) {
    for (int i = 0; i < per; i++) {
      Object object = objects[i];
      Monitor monitor = (Monitor) object;
      if (System.currentTimeMillis() - monitor.getExpireTime() <= 60 * 1000) {
        System.out.print(monitor.getSpanTime() + "ms,");
      }
    }
    System.out.println(" ");
  }

  /** 每5秒钟，打印最近一分钟T99请求耗时 */
  public static void printTP99Monitor() {

    for (Map.Entry<String, ArrayList<Monitor>> entry : timeMap.entrySet()) {
      String key = entry.getKey();
      ArrayList<Monitor> list = entry.getValue();
      Object[] objects = list.toArray();
      Arrays.sort(objects, Collections.reverseOrder());
      int per = (int) (Math.ceil(objects.length * 0.99));
      System.out.println("方法：" + key + " , TP99最近一分钟被调用情况，耗时如下 >>>>>>> ：");
      printTime(objects, per);
    }
  }

  @Override
  public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
    synchronized (TPMonitorFilter.class) {
      new Thread(TPMonitorFilter::printTP90Monitor)
          .start();
    }

    long startTime = System.currentTimeMillis();
    try {
      // 执行方法
      return invoker.invoke(invocation);
    } finally {
      boolean async = RpcUtils.isAsync(invoker.getUrl(), invocation);
      // 判断是否是异步调用
      if (!async) {
        long handleTime = System.currentTimeMillis() - startTime;
        String key = invoker.getInterface().getSimpleName() + "=>" + invocation.getMethodName();
        ArrayList<Monitor> arrayList = timeMap.get(key);
        if (CollectionUtils.isEmpty(arrayList)) {
          arrayList = new ArrayList<>();
        }
        Monitor monitor = new Monitor();
        monitor.setSpanTime(handleTime);
        monitor.setExpireTime(System.currentTimeMillis());
        arrayList.add(monitor);
        timeMap.put(key, arrayList);
      }
    }
  }

  private static class Monitor implements Comparable<Monitor> {
    private long spanTime;
    private long expireTime;

    public long getSpanTime() {
      return spanTime;
    }

    public void setSpanTime(long spanTime) {
      this.spanTime = spanTime;
    }

    public long getExpireTime() {
      return expireTime;
    }

    public void setExpireTime(long expireTime) {
      this.expireTime = expireTime;
    }

    @Override
    public String toString() {
      return "Monitor{" + "spanTime=" + spanTime + ", expireTime=" + expireTime + '}';
    }

    @Override
    public int compareTo(Monitor monitor) {
      return monitor.getSpanTime() > spanTime ? -1 : 0;
    }
  }
}
